#include "SnWindow.h"
#include "GLFW/glfw3.h"
#include "iostream"
#include <memory>
#include <utility>

namespace Sivin {

  class WindHandle {
  public:
    explicit WindHandle() = default;
    ~WindHandle() {
      if (window != nullptr) {
        glfwTerminate();
      }
    }

  public:
    GLFWwindow *window{nullptr};
    std::unique_ptr<SnRender> render{nullptr};
  };

  SnWindow::SnWindow(int width, int height, std::string name)
      : mWidth(width), mHeight(height), mName(name), mWindHandle(new WindHandle()) {
  }

  SnWindow::~SnWindow() {
  }

  static void framebuffer_size_callback(GLFWwindow *window, int width, int height) {
    SnWindow *snWindow = static_cast<SnWindow *>(glfwGetWindowUserPointer(window));
    auto &render = snWindow->getHandle()->render;
    if (render) {
      render->onSurfaceSizeChanged(width, height);
    }
  }


  void debugMessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) {
    printf("OpenGL debug message: %s\n", message);
  }


  void SnWindow::setRender(std::unique_ptr<SnRender> render) {
    mWindHandle->render = std::move(render);
  }

  bool SnWindow::init() {
    glfwInit();
    int majorVerion = 4;
    int minVersion = 5;
#ifdef __APPLE__
    majorVerion = 4;
    minVersion = 1;
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, majorVerion);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, minVersion);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    GLFWwindow *window = glfwCreateWindow(mWidth, mHeight, mName.c_str(), NULL, NULL);
    if (window == nullptr) {
      std::cout << "Failed to create GLFW window" << std::endl;
      glfwTerminate();
      return false;
    }
    glfwMakeContextCurrent(window);
    glfwSetWindowUserPointer(window, this);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    mWindHandle->window = window;

    if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) {
      std::cout << "Failed to initialize GLAD" << std::endl;
      return false;
    }

    //输出OpenGL版本
    const GLubyte *glVersion = glGetString(GL_VERSION);
    std::cout << "OpenGL version:" << glVersion << std::endl;

    // glEnable(GL_DEBUG_OUTPUT);
    // glDebugMessageCallback(debugMessageCallback, 0);

    glViewport(0, 0, mWidth, mHeight);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    if (mWindHandle->render != nullptr) {
      mWindHandle->render->onGLInit();
    }
    return true;
  }


  void SnWindow::show() {
    while (!glfwWindowShouldClose(mWindHandle->window)) {
      //这里的擦除是整个window，并不是仅仅是viewport内
      //viewport作用是OpenGL Draw时的投影区域大小
      glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
      glClear(GL_COLOR_BUFFER_BIT);

      if (mWindHandle->render != nullptr) {
        mWindHandle->render->onRender();
      }

      glfwSwapBuffers(mWindHandle->window);
      glfwPollEvents();
    }
    if (mWindHandle->render != nullptr) {
      mWindHandle->render->onGLDestroy();
    }
  }

}// namespace Sivin
